#include "bsp_iic.h"
#include "bsp_SysTick.h"

/**
 * @brief IIC初始化
 *
 */
void IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_IIC_CLOCK, ENABLE); // 使能 GPIOB 时钟

    // PB6, PB7初始化设置
    GPIO_InitStructure.GPIO_Pin   = SCL_GPIO_Pin | SDA_GPIO_Pin;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;     // 普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;     // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 100MHz
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;      // 上拉
    GPIO_Init(IIC_GPIOx, &GPIO_InitStructure);         // 初始化
    // IIC_SCL = 1;
    // IIC_SDA = 1;
    IIC_SCL_High;
    IIC_SDA_High;
}

/**
 * @brief SDA输出配置
 *
 */
void SDA_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin   = SDA_GPIO_Pin;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;     // 普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;     // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 100MHz
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;      // 上拉
    GPIO_Init(IIC_GPIOx, &GPIO_InitStructure);         // 初始化
}

/**
 * @brief SDA输入配置
 *
 */
void SDA_IN(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin  = SDA_GPIO_Pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 输入模式
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
    GPIO_Init(IIC_GPIOx, &GPIO_InitStructure);   // 初始化
}

/**
 * @brief 产生IIC起始信号
 *
 */
void IIC_Start(void)
{
    SDA_OUT(); // sda线输出
    // IIC_SDA = 1;
    // IIC_SCL = 1;
    IIC_SDA_High;
    IIC_SCL_High;
    delay_us(5);
    // IIC_SDA = 0; // START:when CLK is high,DATA change form high to low
    IIC_SDA_Low; // START:when CLK is high,DATA change form high to low
    delay_us(6);
    // IIC_SCL = 0; // 钳住I2C总线，准备发送或接收数据
    IIC_SCL_Low; // 钳住I2C总线，准备发送或接收数据
}

/**
 * @brief 产生IIC停止信号
 *
 */
void IIC_Stop(void)
{
    SDA_OUT(); // sda线输出
    // IIC_SCL = 0;
    // IIC_SDA = 0; // STOP:when CLK is high DATA change form low to high
    IIC_SCL_Low;
    IIC_SDA_Low; // STOP:when CLK is high DATA change form low to high
    // IIC_SCL = 1;
    IIC_SCL_High;
    delay_us(6);
    // IIC_SDA = 1; // 发送I2C总线结束信号
    IIC_SDA_High;
    delay_us(6);
}

/**
 * @brief 等待应答信号到来
 *
 * @return uint8_t 1 - 接收应答失败
                   0 - 接收应答成功
 */
uint8_t IIC_Wait_Ack(void)
{
    uint8_t tempTime = 0;
    SDA_IN(); // SDA设置为输入
    // IIC_SDA = 1;
    IIC_SDA_High;
    delay_us(1);
    // IIC_SCL = 1;
    IIC_SCL_High;
    delay_us(1);
    while (READ_SDA) {
        tempTime++;
        if (tempTime > 250) {
            IIC_Stop();
            return 1;
        }
    }
    // IIC_SCL = 0; // 时钟输出0
    IIC_SCL_Low;
    return 0;
}

/**
 * @brief 产生ACK应答
 *
 */
void IIC_Ack(void)
{
    // IIC_SCL = 0;
    IIC_SCL_Low;
    SDA_OUT();
    // IIC_SDA = 0;
    IIC_SDA_Low;
    delay_us(2);
    // IIC_SCL = 1;
    IIC_SCL_High;
    delay_us(5);
    // IIC_SCL = 0;
    IIC_SCL_Low;
}

/**
 * @brief 产生NACK非应答
 *
 */
void IIC_NAck(void)
{
    // IIC_SCL = 0;
    IIC_SCL_Low;
    SDA_OUT();
    // IIC_SDA = 1;
    IIC_SDA_High;
    delay_us(2);
    // IIC_SCL = 1;
    IIC_SCL_High;
    delay_us(5);
    // IIC_SCL = 0;
    IIC_SCL_Low;
}

/**
 * @brief IIC发送一个字节
 *
 * @param txd 要发送的一个字节
 */
void IIC_Send_Byte(uint8_t txd)
{
    uint8_t t;
    SDA_OUT();
    // IIC_SCL = 0; // 拉低时钟开始数据传输
    IIC_SCL_Low;
    for (t = 0; t < 8; t++) {
        if ((txd & 0x80) > 0) // 0x80  1000 0000
            // IIC_SDA = 1;
            IIC_SDA_High;
        else
            // IIC_SDA = 0;
            IIC_SDA_Low;
        txd <<= 1;
        delay_us(2); // 对TEA5767这三个延时都是必须的
        // IIC_SCL = 1;
        IIC_SCL_High;
        delay_us(2);
        // IIC_SCL = 0;
        IIC_SCL_Low;
        delay_us(2);
    }
}

/**
 * @brief IIC读一个字节
 *
 * @param ack ack=1时，发送ACK，ack=0，发送nACK
 * @return uint8_t 应答或非应答
 */
uint8_t IIC_Read_Byte(uint8_t ack)
{
    uint8_t i, receive = 0;
    SDA_IN(); // SDA设置为输入
    for (i = 0; i < 8; i++) {
        // IIC_SCL = 0;
        IIC_SCL_Low;
        delay_us(2);
        // IIC_SCL = 1;
        IIC_SCL_High;
        receive <<= 1;
        if (READ_SDA) receive++;
        delay_us(1);
    }
    if (!ack)
        IIC_NAck(); // 发送nACK
    else
        IIC_Ack(); // 发送ACK
    return receive;
}